package org.dfl.core.model.transitions;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.dfl.core.data.connectors.CollectionMapTypeConnector;
import org.dfl.core.data.connectors.CollectionTypeConnector;
import org.dfl.core.data.connectors.TypeConnector;
import org.dfl.core.data.types.Type;
import org.dfl.core.data.types.TypeContainer;
import org.dfl.core.data.types.TypeManager;
import org.dfl.core.model.TransitionExecutionException;
import org.dfl.messages.Messages;

/**
 * transition that creates a cartesian product
 * @author Piotr Wlodarczyk
 *
 */
public class CartesianProductTransition extends MultipleInputsTransition {
	private TypeContainer t1;
	private TypeContainer t2;
	
	@Override
	protected void init() {
		super.init();
		t1 = new TypeContainer();
		t2 = new TypeContainer();
	}
	
	@Override
	protected int getDefaultMaxInputs() {
		return 2;
	}

	@Override
	protected int getDefaultMinInputs() {
		return 2;
	}

	@Override
	protected Type getDefaultOutputDefinition() {
		return TypeManager.getCollectionType(TypeManager.getMapType());
	}
	
	@Override
	public boolean hasFreeInput(Type type) {
		return (super.hasFreeInput(type) && (TypeManager.isAnyType(type) || TypeManager.isCollectionType(type)));
	}
	
	@Override
	public void addInputDefinition(String key, Type type) throws Exception {
		super.addInputDefinition(key, type);
		if(getInputsDefinition().size()==1){
			TypeConnector connector = new CollectionTypeConnector(t1, getInputsDefinition().get(key));
			t1.connectBy(connector);
			connector= new CollectionMapTypeConnector(t1,getOutputTypeContainer(),key);
			getOutputTypeContainer().connectBy(connector);
		}else{
			TypeConnector connector = new CollectionTypeConnector(t2, getInputsDefinition().get(key));
			t2.connectBy(connector);
			connector= new CollectionMapTypeConnector(t2,getOutputTypeContainer(),key);
			getOutputTypeContainer().connectBy(connector);
		}
	}
	
	@Override
	public void removeInputDefinition(String key) {
		if(getInputsDefinition().size()==2){
			t2.separate();
		}else{
			t1.separate();
		}
		super.removeInputDefinition(key);
	}
	
	@SuppressWarnings("unchecked") //$NON-NLS-1$
	@Override
	protected Object fire(Map<String, Object> inputMap) throws TransitionExecutionException {
		Set<String> keys = inputMap.keySet();
		Iterator<String> iter = keys.iterator();
		
		String k1 = iter.next();
		String k2 = iter.next();
		
		Collection<Object> c1 = (Collection<Object>) inputMap.get(k1);
		Collection<Object> c2 = (Collection<Object>) inputMap.get(k2);
		
		Set<Map<String, Object>> ret = new HashSet<Map<String, Object>>();
		
		for(Object e1 : c1){
			for(Object e2 : c2){
				Map<String,Object> couple = new HashMap<String, Object>();
				couple.put(k1,e1);
				couple.put(k2,e2);
				ret.add(couple);
			}
		}
		
		return ret;
	}

	@Override
	public String getDefaultDescription() {
		return Messages.CartesianProductTransition_description;
	}

}
